using System;
using System.Collections.Generic;
using System.Text;

namespace Lutos.CharMutant
{
    public class PinYinCharMutator : ICharMutator
    {
        public static PinYinCharMutator Default;
        public static void InitDefaultMutatorFromFile(string file)
        {
            PinYinMap pym = new PinYinMap();
            System.IO.StreamReader sr = new System.IO.StreamReader(file, Encoding.Default);
            foreach (string s in sr.ReadToEnd().Split('\n'))
            {
                string[] ss = s.Trim().Split(' ');
                int w = int.Parse(ss[0]);
                int i = 0;
                while (ss[++i].Length == 0) ;
                char ch = ss[i][0];
                while (ss[++i].Length == 0) ;
                string py = ss[i];
                pym.AddMap(ch, py, w);
            }
            Default = new PinYinCharMutator(pym);
        }

        public PinYinCharMutator(PinYinMap pym)
        {
            this.PYMap = pym;
        }

        public readonly PinYinMap PYMap;
        public const int MaxMutateRange = 4;
        public const double MaxMutateProportion = 1.0/5.0;
        public int RandomSeed = 0;

        public string Mutate(string text)
        {
            return Mutate(text, RandomSeed);
        }
        public string Mutate(string text, int randseed)
        {
            System.Diagnostics.Trace.WriteLine(string.Format("Mutate: {0}", text));
            List<int> indecis = new List<int>();
            List<int> weighs = new List<int>();
            List<char> chs = new List<char>();
            Random rand = new Random(randseed);
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                string pinyin = PYMap[c];
                if (pinyin != null)
                {
                    IList<PinYinMap.CharWeigh> cws = PYMap[pinyin];
                    int count = cws.Count;
                    if (count > 1)
                    {
                        if (count > MaxMutateRange)
                            count = MaxMutateRange;
                        indecis.Add(i);
                        weighs.Add(PYMap.GetWeigh(c));
                        PinYinMap.CharWeigh cw;
                        do
                        {
                            cw = cws[rand.Next(count)];
                        } while (cw.Ch == c);
                        chs.Add(cw.Ch);
                    }
                }
            }

            if(indecis.Count<0)
            {
                System.Diagnostics.Trace.WriteLine("Can not find any char in same pinyin, try some pinyin like");
                for (int i = 0; i < text.Length; i++)
                {
                    char c = text[i];
                    string pinyin = PYMap[c];
                    if (pinyin != null && char.IsDigit(pinyin, pinyin.Length-1))
                    {
                        for (char d = '1'; d<='4'; d++)
                        {
                            if(d != pinyin[pinyin.Length-1])
                            {
                                string pinyin1 = pinyin.Substring(0, pinyin.Length - 1) + d;
                                IList<PinYinMap.CharWeigh> cws = PYMap[pinyin1];
                                int count = cws.Count;
                                if (count > 0)
                                {
                                    if (count > MaxMutateRange)
                                        count = MaxMutateRange;
                                    indecis.Add(i);
                                    weighs.Add(PYMap.GetWeigh(c));
                                    chs.Add(cws[rand.Next(count)].Ch);
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            string py = null;
            int index = -1;
            int weigh = int.MaxValue;
            if (indecis.Count > 0)
            {
                int x = indecis.Count;
                System.Diagnostics.Trace.WriteLine(string.Format("Get [{0}] count can use", x));
                int[] iis = new int[x];
                int[] ws = weighs.ToArray();
                for (int i = 0; i < x; i++)
                {
                    iis[i] = i;
                }
                Array.Sort(ws,iis);
                int m = (int)(text.Length*MaxMutateProportion);
                if(x>m)
                    x = m;
                index = iis[rand.Next(x)];
                if(index>=0)
                    text = text.Substring(0, indecis[index]) + chs[index] + text.Substring(indecis[index]+1);
                System.Diagnostics.Trace.WriteLine(string.Format("return \"{0}\"", text));
                return text;
            }
            System.Diagnostics.Trace.WriteLine("Can not find any pinyin like, try replace pinyin");
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                if (PYMap.ContainsChar(c))
                {
                    int w = PYMap.GetWeigh(c);
                    if (w < weigh)
                    {
                        weigh = w;
                        index = i;
                        py = PYMap[c];
                    }
                }
            }
            if (index >= 0)
            {
                while (py.Length > 0 && char.IsDigit(py, py.Length - 1))
                    py = py.Substring(0, py.Length - 1);
                text = text.Substring(0, index) + py + text.Substring(index + 1);
            }
            System.Diagnostics.Trace.WriteLine(string.Format("return \"{0}\"", text));
            return text;
        }
    }
}
